﻿using System;
using System.Collections.Generic;
using System.Drawing.Printing;
using System.Drawing;
using System.Windows.Forms;

namespace KillBill.Presentarion.Common.Printing
{
    public class Report
    {
        #region variables
        int page = 0;
        private bool isPreview = false;
        private System.Windows.Forms.PrintDialog printDialog = new PrintDialog();
        private System.Windows.Forms.PrintPreviewDialog preview = new PrintPreviewDialog();
        private static string companyName = "";
        private Image reportImage;
        private static Image companyLogo;
        private static List<string> companyData = new List<string>();
        private PrintDocument document = new PrintDocument();
        private bool allPagesHeader = false;
        private string reportName = "";
        private string footer = "";
        private int spacing = 2;
        private List<ColumnFormat> columnsFormat = new List<ColumnFormat>();
        private List<object[]> dataTable = new List<object[]>();
        private Queue<object[]> data = new Queue<object[]>();
        private Font fontCompanyName = new Font("Cambria", 20);
        private Font fontReportName = new Font("Cambria", 14);
        private Font fontCompanyData = new Font("Cambria", 9);
        private DateTime printDate;
        private int imageHeight;

        #endregion
        #region Properties
        public int Spacing
        {
            get { return spacing; }
            set { spacing = value; }
        }
        public static string CompanyName
        {
            get { return companyName; }
            set { companyName = value; }
        }
        public string _companyName
        {
            get { return CompanyName; }
            set { CompanyName = value; }
        }
        public static List<string> CompanyData
        {
            get { return companyData; }
            set { companyData = value; }
        }
        public List<string> _companyData
        {
            get { return CompanyData; }
            set { CompanyData = value; }
        }
        /// <summary>
        /// Reporte Gáfico
        /// </summary>
        /// <param name="image">(Gráfico de barras, tendencias, pastel, etc.</param>
        /// <param name="height">Altura del grafico en Px</param>
        public void setReportImage(Image image, int height)
        {
            reportImage = image;
            imageHeight = height;
        }
        private void setReportImage(Image image)
        {
            reportImage = image;
            imageHeight = image.Height;
        }
        /// <summary>
        /// Reporte Gráfico, se imprimiran con las medidas del gráfico, para redimensionar la imagen utilizar 
        /// setReportImage(Image image, int height)
        /// </summary>
        public Image ReportImage
        {
            get { return reportImage; }
            set { setReportImage(value); }
        }
        public string ReportName
        {
            get { return reportName; }
            set { reportName = value; }
        }
        public bool AllPagesHeader
        {
            get { return allPagesHeader; }
            set { allPagesHeader = value; }
        }
        public string Footer
        {
            get { return footer; }
            set { footer = value; }
        }
        public List<ColumnFormat> ColumnsFormat
        {
            get { return columnsFormat; }
            set { columnsFormat = value; }
        }
        public List<object[]> DataTable
        {
            set { dataTable = value; }
            get { return dataTable; }
        }
        #endregion
        #region Methods
        public void fromGridView(System.Windows.Forms.DataGridView  dataGrid)
        {
            
            dataTable = new List<object[]>();
            for (int i = 0; i < dataGrid.RowCount; i++)
            {
                object[] row = new object[dataGrid.ColumnCount];
                for (int j = 0; j < dataGrid.ColumnCount; j++)
                {
                    row[j] = dataGrid[j, i].Value ;
                }
                dataTable.Add(row);
            }
        }
        private void obtainData()
        {
            data = new Queue<object[]>();
            foreach (object[] insert in dataTable)
                data.Enqueue(insert);
        }
        public void showPreView()
        {
            isPreview = true;
            if (dataTable != null && columnsFormat != null)
            {

            }
        }
        public void PrintReport()
        {
            printDate = DateTime.Now;
            obtainData();
            isPreview = false;
            if (printDialog.ShowDialog() != DialogResult.Cancel)
            {
                document.PrinterSettings = printDialog.PrinterSettings;
                document.PrintPage += new PrintPageEventHandler(document_PrintPage);
                document.Print();
            }
        }
        private int getPosition(int minX, int maxX, string text, Font font, Formater.position position)
        {
            int result;
            if (position == Formater.position.left)
            {
                return minX;
            }
            else if (position == Formater.position.center)
            {
                result = maxX - minX - TextRenderer.MeasureText(text, font).Width;
                result = (int)(result / 2);
            }
            else
            {
                result = maxX - minX - TextRenderer.MeasureText(text, font).Width;
            }
            return result + minX;
        }
        #endregion
        private void document_PrintPage(object sender, PrintPageEventArgs ev)
        {
            #region InternalVariables
            page++;
            Pen drawingPen = new Pen(Brushes.Black, 1);
            Font currFont = fontCompanyName;
            int currY = ev.MarginBounds.Top;
            int currX = ev.MarginBounds.Left;
            string line = "";
            bool isFoterDone = false;
            #endregion

            #region Header
            if (AllPagesHeader || page == 1)
            {
                ev.Graphics.DrawString(CompanyName, currFont, drawingPen.Brush, new PointF(currX, currY));
                currY += TextRenderer.MeasureText("I", currFont).Height;
                currFont = fontCompanyData;
                foreach (string extraData in CompanyData)
                {
                    ev.Graphics.DrawString(extraData, currFont, drawingPen.Brush, new Point(currX, currY));
                    currY += TextRenderer.MeasureText("I", currFont).Height;
                }
                currY += TextRenderer.MeasureText("I", currFont).Height;

                currX = getPosition(ev.MarginBounds.Left, ev.MarginBounds.Right, ReportName, fontReportName, Formater.position.center);
                ev.Graphics.DrawString(ReportName, fontReportName, drawingPen.Brush, new Point(currX, currY));
                currY += TextRenderer.MeasureText("I", currFont).Height;
                currX = getPosition(ev.MarginBounds.Left, ev.MarginBounds.Right, "Fecha: " + Formater.getStandarDate(printDate), currFont, Formater.position.right);
                ev.Graphics.DrawString("Fecha: " + Formater.getStandarDate(printDate), currFont, drawingPen.Brush, new Point(currX, currY));
                currY += TextRenderer.MeasureText("I", currFont).Height;
                currX = getPosition(ev.MarginBounds.Left, ev.MarginBounds.Right, "Hora: " + Formater.getHour(printDate), currFont, Formater.position.right);
                ev.Graphics.DrawString("Hora: " + Formater.getHour(printDate), currFont, drawingPen.Brush, new Point(currX, currY));
                currY += TextRenderer.MeasureText("I", currFont).Height;
                currY += TextRenderer.MeasureText("I", currFont).Height;
            }
            #endregion

            #region Images
            if (page == 1 && reportImage != null)
            {
                int Width = (int)((reportImage.Width * imageHeight) / reportImage.Height);
                currX = (int)((ev.MarginBounds.Right - ev.MarginBounds.Left - Width) / 2);
                currX += ev.MarginBounds.Left;
                ev.Graphics.DrawImage(reportImage, currX, currY, Width, imageHeight);
                currY += imageHeight ;
                currY += TextRenderer.MeasureText("I", currFont).Height;
            }
            #endregion

            if (data.Count > 0)
            {
                #region ColumnNames
                currFont = new Font("Courier New", 8, FontStyle.Bold);
                line = "";
                string subline = "";
                int countColumns = 0;
                int limit = ColumnsFormat.Count;
                foreach (ColumnFormat format in ColumnsFormat)
                {
                    countColumns++;
                    line += Formater.standardizeString(format.Text, format.Position, format.Length);
                    subline += Formater.standardUnderlineString(format.Length);
                    if (countColumns != limit)
                    {
                        line += Formater.SPC(Spacing);
                        subline += Formater.SPC(Spacing);
                    }
                }
                currX = getPosition(ev.MarginBounds.Left, ev.MarginBounds.Right, line, currFont, Formater.position.center);
                ev.Graphics.DrawString(line, currFont, drawingPen.Brush, new Point(currX, currY));
                ev.Graphics.DrawString(subline, currFont, drawingPen.Brush, new Point(currX, currY));
                currY += TextRenderer.MeasureText("I", currFont).Height;
                #endregion

                #region DataTable
                currFont = new Font("Courier New", 8);
                while (data.Count > 0)
                {
                    countColumns = 0;
                    object[] elements = data.Dequeue();
                    if ((currY + TextRenderer.MeasureText("I", fontCompanyData).Height) > (ev.MarginBounds.Y + ev.MarginBounds.Height))
                    {
                        #region footer
                        isFoterDone = true;
                        currX = getPosition(ev.MarginBounds.Left, ev.MarginBounds.Right, "Continua...", fontCompanyData, Formater.position.right);
                        ev.Graphics.DrawString("Continua...", fontCompanyData, drawingPen.Brush, new Point(currX, currY));
                        currY += TextRenderer.MeasureText("I", currFont).Height;
                        ev.Graphics.DrawLine(drawingPen, new Point(ev.MarginBounds.X, currY), new Point(ev.MarginBounds.X + ev.MarginBounds.Width, currY));
                        currY += TextRenderer.MeasureText("I", currFont).Height;
                        currX = getPosition(ev.MarginBounds.Left, ev.MarginBounds.Right + ev.MarginBounds.Width, footer, fontCompanyData, Formater.position.left);
                        ev.Graphics.DrawString(Footer, fontCompanyData, drawingPen.Brush, new Point(currX, currY));
                        currX = getPosition(ev.MarginBounds.Left, ev.MarginBounds.Right, "-" + page + "-", fontCompanyData, Formater.position.right);
                        ev.Graphics.DrawString("-" + page + "-", fontCompanyData, drawingPen.Brush, new Point(currX, currY));
                        #endregion

                        ev.HasMorePages = true;
                        break;
                    }
                    line = "";
                    foreach (ColumnFormat format in ColumnsFormat)
                    {
                        countColumns++;
                        if (format.IsNumber)
                            line += Formater.standardizeNumber(Convert.ToDouble(elements[countColumns - 1]), format.Presition, format.Length);
                        else
                            line += Formater.standardizeString(Convert.ToString(elements[countColumns - 1]), format.Position, format.Length);
                        if (countColumns != limit)
                        {
                            line += Formater.SPC(Spacing);
                        }
                    }
                    ev.Graphics.DrawString(line, currFont, drawingPen.Brush, new Point(currX, currY));
                    currY += TextRenderer.MeasureText("I", currFont).Height;
                }
                #endregion
            }
            #region Footer
            if (!isFoterDone)
            {
                while (currY < (ev.MarginBounds.Y + ev.MarginBounds.Height))
                {
                    currY += TextRenderer.MeasureText("I", currFont).Height;
                }
                currY += TextRenderer.MeasureText("I", currFont).Height;
                ev.Graphics.DrawLine(drawingPen, new Point(ev.MarginBounds.X, currY), new Point(ev.MarginBounds.X + ev.MarginBounds.Width, currY));
                currY += TextRenderer.MeasureText("I", currFont).Height;
                currX = getPosition(ev.MarginBounds.X, ev.MarginBounds.X + ev.MarginBounds.Width, footer, fontCompanyData, Formater.position.left);
                ev.Graphics.DrawString(Footer, fontCompanyData, drawingPen.Brush, new Point(currX, currY));
                currX = getPosition(ev.MarginBounds.X, ev.MarginBounds.X + ev.MarginBounds.Width, "-" + page + "-", fontCompanyData, Formater.position.right);
                ev.Graphics.DrawString("-" + page + "-", fontCompanyData, drawingPen.Brush, new Point(currX, currY));
            }
            #endregion

        }
    }
    public class Formater
    {
        #region STRING STANDAR
        public enum position : int
        {
            left = 0,
            right = 1,
            center = 2
        }
        public static string getStandarDate(DateTime date)
        {
            return date.ToShortDateString();
        }
        public static string getHour(DateTime date)
        {
            return date.ToLongTimeString();
        }
        public static string standardizeNumber(double value, int presition, int fieldLength)
        {
            string intSection = "";
            string decSection = "";
            string[] values = (value + "").Trim().Replace('.', ',').Split(",".ToCharArray());
            intSection = values[0];
            if (values.Length == 1)
                while (decSection.Length < presition)
                    decSection += "0";
            else if (values.Length == 2)
            {
                decSection = values[1];
                if (decSection.Length > presition)
                    decSection = decSection.Substring(0, presition);
                else if (decSection.Length < presition)
                    while (decSection.Length < presition)
                        decSection += "0";
            }
            return standardizeString(intSection + "," + decSection, position.right, fieldLength);
        }
        public static string standardUnderlineString(int length)
        {
            string output = "";
            while (output.Length < length)
                output += "_";
            return output;
        }
        public static string SPC(int length)
        {
            string output = "";
            while (output.Length < length)
                output += " ";
            return output;
        }
        public static string standardizeString(string inputString, position alignment, int length)
        {
            string outputString = inputString;
            if (outputString.Length > length)
            {
                outputString = outputString.Substring(0, length);
            }
            else if (outputString.Length < length)
            {
                if (alignment == position.left)
                    while (outputString.Length < length)
                        outputString += " ";
                else if (alignment == position.right)
                    while (outputString.Length < length)
                        outputString = " " + outputString;
                else
                {
                    int i = 0;
                    while (outputString.Length < length)
                    {
                        if (i % 2 == 0)
                            outputString = " " + outputString;
                        else
                            outputString += " ";
                        i++;
                    }
                }
            }
            return outputString;
        }
        #endregion
    }
    public class ColumnFormat
    {
        private string text;
        private bool isNumber;
        private int presition;
        private int length;
        private Formater.position position = Formater.position.left;

        public Formater.position Position
        {
            get { return position; }
            set { position = value; }
        }
        public string Text
        {
            get { return text; }
            set { text = value; }
        }
        public bool IsNumber
        {
            get { return isNumber; }
            set { isNumber = value; }
        }
        public int Presition
        {
            get { return presition; }
            set { presition = value; }
        }
        public int Length
        {
            get { return length; }
            set { length = value; }
        }

        /// <summary>
        /// Formato de texto predeterminado (alineación izquierda)
        /// </summary>
        /// <param name="text"></param>
        /// <param name="lenght"></param>
        public ColumnFormat(string columnText, int columnLenght)
        {
            isNumber = false;
            this.text = columnText;
            this.length = columnLenght;
            this.presition = 0;
            position = Formater.position.left;
        }
        /// <summary>
        /// Formato de Texto
        /// </summary>
        /// <param name="columnText"></param>
        /// <param name="columnLenght"></param>
        /// <param name="aligment"></param>
        public ColumnFormat(string columnText, int columnLenght, Formater.position aligment)
        {
            this.isNumber = false;
            this.text = columnText;
            this.length = columnLenght;
            this.position = aligment;
            this.presition = 0;
        }
        /// <summary>
        /// Formato de numeros
        /// </summary>
        /// <param name="columnNumber"></param>
        /// <param name="presition"></param>
        /// <param name="columnLenght"></param>
        public ColumnFormat(string columnNumber, int presition, int columnLenght)
        {
            this.text = columnNumber;
            this.isNumber = true;
            this.presition = presition;
            this.length = columnLenght;
            this.position = Formater.position.right;
        }
    }
}
